﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Geometry;
using System.Windows.Forms;
using System.Reflection;
//using Autodesk.AutoCAD.Interop;

namespace ZDMHelper
{
    public class CommandMethods
    {
        private Database database = HostApplicationServices.WorkingDatabase;
        private Editor editor = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
        private CADOperations cad;
        private ExcelOperations excel;
        private ObjectId ceLayer;
        private ObjectId oeLayer;
        private ObjectId ieLayer;
        private ObjectId reLayer;
        private ObjectId textStyle;

        //private Point3d basePoint = new Point3d();
        private LineDirection direction = LineDirection.LeftToRight;

        private List<ChainScission> chains = new List<ChainScission>();
        private List<BrokenHeight> heights = new List<BrokenHeight>();

        [CommandMethod("CSH")]
        public void Initialization()
        {
            FormInitialization form = new FormInitialization();
            DialogResult dr = Autodesk.AutoCAD.ApplicationServices.Application.ShowModalDialog(form);
            if (dr == DialogResult.OK)
            {
                try
                {
                    this.excel = new ExcelOperations(form.XLSFileName);
                    this.excel.errorHandler += new ExcelOperations.ErrorHandler(excel_errorHandler);
                    chains = this.excel.ReadChainScissions();
                    heights = this.excel.ReadBrokenHeights();
                }
                catch
                {
                    editor.WriteMessage("Excel文件打开错误，确保文件存在并处于关闭状态！");
                }

                this.direction = form.Direction;
                cad = new CADOperations(database, editor);
                ceLayer = cad.CreareLayer("控制标高", 1);
                oeLayer = cad.CreareLayer("路面标高", 2);
                ieLayer = cad.CreareLayer("百年水位", 4);
                reLayer = cad.CreareLayer("行政区划", 7);
                textStyle = cad.CreareTextStyle("宋体1");
            }
        }

        [CommandMethod("BZBG")]
        public void LabelElevation()
        {
            if (!IsInitialized())
            {
                editor.WriteMessage("请先初始化[CSH]，读入数据文件！");
                return;
            }
            FormElevation form = new FormElevation();
            DialogResult dr = Autodesk.AutoCAD.ApplicationServices.Application.ShowModalDialog(form);
            if (dr == DialogResult.OK)
            {
                List<ElevationData> datas = this.excel.ReadElevationDatas();
                foreach (ElevationData data in datas)
                {
                    CreateElevation(data, form.BasePoint, form.DrawControl, form.DrawLoad, form.DrawRiver);
                }
            }
        }

        void excel_errorHandler(string message)
        {
            //throw new NotImplementedException();
            editor.WriteMessage(message);
        }

        [CommandMethod("BZQH")]
        public void LabelRegion()
        {
            if (!IsInitialized())
            {
                editor.WriteMessage("请先初始化[CSH]，读入数据文件！");
                return;
            }
            FormRegion form = new FormRegion();
            DialogResult dr = Autodesk.AutoCAD.ApplicationServices.Application.ShowModalDialog(form);
            if (dr == DialogResult.OK)
            {
                List<RegionData> datas = this.excel.ReadRegionDatas();
                foreach (RegionData data in datas)
                {
                    CreateRegion(data, form.TopPoint);
                }
            }
        }

        /// <summary>
        /// 绘制控制标高
        /// </summary>
        /// <param name="data">控制标高数据</param>
        /// <param name="basePoint">基点</param>
        /// <param name="drawControlEle">是否绘制控制标高</param>
        /// <param name="drawLoadEle">是否绘制路面标高</param>
        /// <param name="drawRiverEle">是否绘制百年水位</param>
        private void CreateElevation(ElevationData data, Point3d basePoint, bool drawControlEle, bool drawLoadEle, bool drawRiverEle)
        {
            double realLength = GetRealLength(data.Symbol, data.Mileage);
            if (realLength == -0.1)
            {
                //提示里程或冠号错误
            }
            else
            {
                //获取基点高程
                double baseElevation = GetBaseElevation(data.Symbol, data.Mileage);
                //计算控制标高、路面或百年水位高度
                double controlHeight = data.ControlElvation - baseElevation;
                double baseHeight = data.BaseElvation - baseElevation;

                Point3d csPos = new Point3d(basePoint.X + realLength / 10, basePoint.Y + controlHeight, 0);
                Point3d bsPos = new Point3d(basePoint.X + realLength / 10, basePoint.Y + baseHeight, 0);
                if (drawControlEle)
                {
                    cad.DrawControlElevation(ceLayer, textStyle, csPos, direction, data.ControlElvation);
                }
                if (data.FlagType == Flag.Load && drawLoadEle)
                {
                    cad.DrawLoadElevation(oeLayer, textStyle, bsPos, direction, data.BaseElvation);
                }
                if (data.FlagType == Flag.Rivers && drawRiverEle)
                {
                    cad.DrawRiverElevation(ieLayer, textStyle, bsPos, direction, data.BaseElvation);
                }
            }
        }

        /// <summary>
        /// 绘制行政区划
        /// </summary>
        /// <param name="data">行政区划区划</param>
        /// <param name="topPoint">顶点</param>
        private void CreateRegion(RegionData data, Point3d topPoint)
        {
            //判断里程为与那个区间
            double realLength = GetRealLength(data.Symbol, data.Mileage);
            if (realLength == -0.1)
            {
                //提示里程或冠号错误
            }
            else
            {
                Point3d position = new Point3d(topPoint.X + realLength / 10, topPoint.Y, 0);
                cad.DrawRegion(reLayer, textStyle, position, data);
            }
        }

        /// <summary>
        /// 判断里程位于哪个区间
        /// </summary>
        /// <param name="symbol">冠号</param>
        /// <param name="mileage">里程</param>
        /// <returns>区间标号，-1表示该里程不存在或冠号有误，可根据标号计算断链总长度</returns>
        private int InWhichChainSection(string symbol, double mileage)
        {
            int index = -1;
            for (int i = 0; i < chains.Count; i++)
            {
                ChainScission chain1 = chains[i];
                ChainScission chain2 = chains[i + 1];
                if (symbol == chain1.BackSymbol && mileage >= chain1.BackMileage && mileage < chain2.FrontMileage)
                {
                    index = i;
                    break;
                }
            }
            return index;
        }

        /// <summary>
        /// 处理断链，计算断链总长度
        /// </summary>
        /// <param name="index">里程区间标号</param>
        /// <returns>断链总长度</returns>
        private double GetTotalChainLength(int index)
        {
            double totalChainLength = 0.0;
            for (int i = 0; i < index + 1; i++)
            {
                //纵断面断链长度处理，还需实际调试
                double y = chains[i].ChainLength % 100;
                //长链
                if (chains[i].ChainLength > 0)
                {
                    if (y >= 50)
                    {
                        totalChainLength += chains[i].ChainLength - y + 100;

                    }
                    else
                    {
                        totalChainLength += chains[i].ChainLength - y;
                    }
                }
                //短链
                else
                {
                    if (y <= -50)
                    {
                        totalChainLength += chains[i].ChainLength - y - 100;
                    }
                    else
                    {
                        totalChainLength += chains[i].ChainLength - y;
                    }
                }

            }
            return totalChainLength;
        }

        /// <summary>
        /// 计算考虑断链后的实际长度
        /// </summary>
        /// <param name="symbol">冠号</param>
        /// <param name="mileage">里程</param>
        /// <returns>实际长度</returns>
        private double GetRealLength(string symbol, double mileage)
        {
            double realLength = -0.1;
            int index = InWhichChainSection(symbol, mileage);
            if (index == -1)
            {
                //提示里程或冠号错误
            }
            else
            {
                //计算总断链长度
                double totalChainLength = GetTotalChainLength(index);
                //根据断链长度，起点里程，当前里程计算实际长度
                realLength = mileage - chains[0].FrontMileage + totalChainLength;
                if (this.direction == LineDirection.RightToLeft)
                {
                    realLength = -realLength;
                }
            }
            return realLength;
        }

        /// <summary>
        /// 获取基准标高
        /// </summary>
        /// <param name="symbol">冠号</param>
        /// <param name="mileage">里程</param>
        /// <returns>基准高程</returns>
        private double GetBaseElevation(string symbol, double mileage)
        {
            double elevation = 0.0;
            for (int i = 0; i < heights.Count; i++)
            {
                BrokenHeight height = heights[i];
                //无需比较冠号，但碰到标尺正好在长链处时有问题
                //避免标尺与长链重合
                if (mileage < height.Mileage)
                {
                    //index = i;
                    elevation = heights[i - 1].Elvation;
                    break;
                }
                
            }
            return elevation;
        }

        /// <summary>
        /// 判断是否已初始化
        /// </summary>
        /// <returns>是否已初始化</returns>
        private bool IsInitialized()
        {
            return (this.cad != null);
        }

        [CommandMethod("GJL")]
        public void AddToolBar()
        {
            //Module module = Assembly.GetExecutingAssembly().GetModules()[0];
            //string str = module.FullyQualifiedName;
            //AcadMenuGroup group = 
            //Autodesk.AutoCAD.Interop
        }

        [CommandMethod("test")]
        public void test()
        {
            //还需考虑绘制方向
            Point3d basePoint = new Point3d();
            Point3d topPoint = new Point3d();
            //绘制横线
            for (int i = 1; i < heights.Count; i++)
            {
                BrokenHeight height = heights[i];
                double realLength = GetRealLength(height.Symbol, height.Mileage);
                Point3d firstPoint = GetFirstPoint(basePoint, heights[i - 1].Elvation);
                //最后一个 还需与顶点比较
                if (i == heights.Count - 1)
                {
                    realLength = (topPoint.X - firstPoint.X) * 10;
                }
                while (firstPoint.Y < topPoint.Y) 
                {
                    Point3d endPoint = new Point3d(firstPoint.X + realLength / 10, firstPoint.Y, 0);
                    //draw line
                    cad.DrawGridLine(firstPoint, endPoint);
                    firstPoint = new Point3d(firstPoint.X, firstPoint.Y + 50, 0);
                }
            }
            //绘制竖线
            for (int i = 1; i < chains.Count; i++)
            {
                ChainScission chain = chains[i];
                double h = topPoint.Y - basePoint.Y;
                double realLength = GetRealLength(chains[i + 1].BackSymbol, chains[i + 1].BackMileage);//???
                Point3d firstPoint = GetFirstPoint(basePoint, chain);
                realLength += firstPoint.X;
                while (firstPoint.X < realLength)
                {
                    Point3d endPoint = new Point3d(firstPoint.X, firstPoint.Y + h, 0);
                    //draw line
                    cad.DrawGridLine(firstPoint, endPoint);
                    firstPoint = new Point3d(firstPoint.X, firstPoint.Y + 50, 0);
                }
            }
        }

        private Point3d GetFirstPoint(Point3d basePoint, double baseElevation)
        {
            int index = (int)(baseElevation % 50 / 10);
            return new Point3d(basePoint.X, basePoint.Y + (5 - index) * 10, 0);
        }

        private Point3d GetFirstPoint(Point3d basePoint, ChainScission chain)
        {
            int index = (int)(chain.BackMileage % 500 / 100);
            return new Point3d(basePoint.X + (5 - index) * 10, basePoint.Y, 0);
        }
    }
}
